package core

import (
	"errors"
	"github.com/robfig/cron/v3"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sync"
	"sync/atomic"
)

// 服务注册表
// map[string]*LoadBalance{ServerSlice}
// 第一层 map 为服务隔离 key -> 服务名称 value -> 负载均衡结构体 (当前注册的服务)
// 第二层 *LoadBalance 负载均衡 内置 ServerSlice 集群服务切片
var service sync.Map

// 代理转发
type proxy httputil.ReverseProxy

func (p *proxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	var reverse *httputil.ReverseProxy
	reverse.ServeHTTP(rw, req)
}

// ProxyServer 代理服务
type server struct {
	InstanceId       string                 `json:"instanceId"`     //实例ID
	Name             string                 `json:"name"`           //服务名称
	Scheme           string                 `json:"scheme"`         //服务协议
	Host             string                 `json:"host"`           //服务地址
	Port             string                 `json:"port"`           //服务端口
	Prefix           string                 `json:"prefix"`         //前置地址
	PreservePrefix   int                    `json:"preservePrefix"` //是否保留前缀
	HealthyTime      int64                  `json:"healthyTime"`    //每次健康轮训时间间隔 秒
	Weight           int64                  `json:"weight"`         //权重
	proxy            *httputil.ReverseProxy //服务代理
	addr             *url.URL               //服务url
	nextHealthyTimes int64                  //下一次检查时间戳
	nextFailureTimes int64                  //下一次失效检查时间戳
	currentWeight    int64                  //当前权重
	FailureWeight    int64                  `json:"failureWeight"` //失效权重
	FailureFactor    float64                `json:"failureFactor"` //降权因子
	index            int                    //当前实例序号
	Volume           int64                  `json:"volume"` //当前请求总数
}

// ServerSlice 服务切片
type ServerSlice []*server

func (x ServerSlice) Len() int { return len(x) }
func (x ServerSlice) Less(i, j int) bool {
	if x[i].currentWeight == x[j].currentWeight {
		return x[i].index > x[j].index
	}
	return x[i].currentWeight > x[j].currentWeight
}
func (x ServerSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }

// Register
//
//	@Description: 注册服务
//	@receiver this
func (this *server) register() error {
	if this.Prefix == "admin" {
		return errors.New("admin is reserved for the system")
	}
	addr, err := url.Parse(this.Scheme + "://" + this.Host + ":" + this.Port)
	if err != nil {
		return err
	}
	this.addr = addr
	//初始化代理器
	reverseProxy := httputil.NewSingleHostReverseProxy(this.addr)
	this.proxy = reverseProxy
	//初始化实例Id
	this.getClientId()
	//初始化降权因子
	if this.FailureFactor == 0 {
		this.FailureFactor = DefaultFailureFactor
	}
	//初始化下一次失效检查时间戳
	this.nextFailureTimes = 0

	//首先判断是否存在该服务
	instance, loadBalance := this.getLbAndInstance()
	//如果存在负载均衡就加写锁
	if loadBalance != nil {
		loadBalance.instanceRWLock.Lock()
		//释放写锁
		defer loadBalance.instanceRWLock.Unlock()
	}
	if instance != nil { //如果存在服务直接覆盖
		*instance = *this
	}
	if loadBalance != nil && instance == nil { //如果有服务注册表 但是 无实例
		loadBalance.Servers = append(loadBalance.Servers, this)
	}
	if loadBalance == nil { //无服务注册表
		loadBalance = &LoadBalance{
			Servers: []*server{this},
			task:    cron.New(cron.WithSeconds()),
			key:     this.Prefix,
		}
		loadBalance.AddHealthyTask() //添加健康任务
		loadBalance.task.Start()     //启动任务
		service.Store(this.Prefix, loadBalance)
	}
	if this.Weight == 0 {
		this.Weight = 1
	}
	this.index = loadBalance.Servers.Len() + 1
	//更新总权重
	loadBalance.sumWeight()
	return nil
}

// getLbAndInstance
//
//	@Description: 是否存在该实例
//	@receiver this
//	@return *server
//	@return int 1 存在 0 有对应服务注册表但无该实例 -1 无对应服务注册表
func (this *server) getLbAndInstance() (*server, *LoadBalance) {
	//是否存在对应的服务
	loadBalance, isLoadBalanc := service.Load(this.Prefix)
	if isLoadBalanc {
		//转成负载均衡结构体
		lb := loadBalance.(*LoadBalance)
		//判断是否有该实例
		for _, s := range lb.Servers {
			if s.InstanceId == this.InstanceId {
				return s, lb
			}
		}
		return nil, lb
	}
	return nil, nil
}

// getClientId
//
//	@Description: 获取实例ID
//	@receiver this
//	@return string
func (this *server) getClientId() string {
	host := this.Host
	port := this.Port
	this.InstanceId = "Instance@" + host + ":" + port
	return this.InstanceId
}

// CheckOrRegister
//
//	@Description: 检查如果不存在则注册服务
//	@receiver this
func (this *server) CheckOrRegister() string {
	this.getClientId()
	instance, _ := this.getLbAndInstance()
	//如果找到则更新下一次检查时间戳
	if instance != nil {
		instance.nextHealthyTimes = this.nextHealthyTimes
		instance.FailureWeight = 0    //重置失效权重
		instance.nextFailureTimes = 0 //重置下一次检查失效时间戳
		return "success"
	}
	return "uninstance"
}

// getInstance
//
//	@Description: 获取实例对象
//	@return *server
func getInstance(proxy string) (*server, error) {
	loadBalance, isLoadBalanc := service.Load(proxy)
	if !isLoadBalanc {
		return nil, errors.New("no Instance [" + proxy + "]")
	}
	lb := loadBalance.(*LoadBalance)
	instance := lb.loadLoop()
	return instance, nil
}

// send
//
//	@Description: 转发请求
//	@param proxy
//	@param w
//	@param r
func send(proxy string, w http.ResponseWriter, r *http.Request) {
	instance, err := getInstance(proxy)
	if err != nil {
		log.Println(err.Error())
		return
	}
	if instance == nil {
		log.Println("instance is nil")
		return
	}
	//是否保留前缀
	if instance.PreservePrefix == 0 {
		r.URL.Path = r.URL.Path[len(instance.Prefix)+1:]
	}
	log.Println(instance.InstanceId + "=>[" + instance.Name + "]=>" + r.URL.Path)
	instance.proxy.ServeHTTP(w, r)
	atomic.AddInt64(&instance.Volume, 1)
}
